/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package mysgoog.bot.algo.territory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import mysgoog.board.Board;
import mysgoog.bot.Config;
import mysgoog.bot.algo.territory.shape.Shape;
import mysgoog.brown.Lib;
import mysgoog.gtp.Color;

/**
 *
 * @author Wang Xin'an
 */
public class TerritoryEvaluator {

	private static final Board dumbBrd = Config.getEmptyBoard();
	private static final int MAX_SIZE = dumbBrd.BOARD_ELEMNUM;

	private static final int EMPTY = 0, FULL_EYE = 1, PARTIAL_EYE = 2,
			FALSE_EYE = 3, BLACK = 4, WHITE = 5;

	// the territory
	private static boolean[] territory = new boolean[MAX_SIZE]; // to decide
	// whether it's
	// in the
	// territory
	private static LinkedList<Integer> terrList = null;
	private static int[] board = new int[MAX_SIZE];
	private static int mycolor, oppcolor;

	/*
	 * input: brd ----- current board poss ---- all the positions of the
	 * territory, can be emmpty, black ,white color ---- my color (Black , or
	 * White) output : the state of the territor:
	 */
	static public State evaluate(Board brd, LinkedList<Integer> poss,
			Color color) {
		initial(brd, poss, color);
		buildFalseEye();
		buildPartialEye();
		buildFullEye();
		return getState();
	}

	/*
	 * input: board and the positions of the territory
	 */
	static private void initial(Board brd, LinkedList<Integer> poss, Color color) {
		terrList = poss;
		for (int i = 0; i < MAX_SIZE; ++i) {
			Color c = brd.getStone(i);
			if (c == Color.EMPTY) {
				board[i] = EMPTY;
			} else if (c == Color.BLACK) {
				board[i] = BLACK;
			} else {
				board[i] = WHITE;
			}
		}

		Arrays.fill(territory, false);
		for (Integer pos : poss) {
			territory[pos] = true;
		}

		if (color == Color.BLACK) {
			mycolor = BLACK;
			oppcolor = WHITE;
		} else {
			mycolor = WHITE;
			oppcolor = BLACK;
		}

	}

	static private int[] digI = { 1, -1, 1, -1 };
	static private int[] digJ = { 1, 1, -1, -1 };

	static private void buildFalseEye() {
		do {
			int count = 0;
			for (Integer pos : terrList) {
				if (board[pos] == EMPTY) {
					ArrayList<Integer> adj = dumbBrd.getAdjacent(pos);
					for (Integer adjpos : adj) {
						if (board[adjpos] == oppcolor) {
							board[pos] = FALSE_EYE;
							++count;
							break;
						}
					}
				}

				if (board[pos] == EMPTY) {
					int ax = dumbBrd.posX(pos);
					int ay = dumbBrd.posY(pos);

					int digN = 0;
					int oppN = 0;

					for (int k = 0; k < 4; ++k) {
						int apos = dumbBrd.makePos(ax + digI[k], ay + digJ[k]);
						if (dumbBrd.inBoard(apos)) {
							++digN;
							if (board[apos] == EMPTY
									|| board[apos] == FALSE_EYE) {
								++oppN;
							}
						}
					}
					if (digN <= 2) {
						if (oppN >= digN / 2) {
							board[pos] = FALSE_EYE;
							++count;
						}
					} else {
						if (oppN > digN / 2) {
							board[pos] = FALSE_EYE;
							++count;
						}
					}
				}
			}
			if (count == 0)
				break;

		} while (true);
	}

	static private void buildPartialEye() {
		for (Integer pos : terrList) {
			if (board[pos] == EMPTY) {
				ArrayList<Integer> adj = dumbBrd.getAdjacent(pos);
				for (Integer adjpos : adj) {
					if (board[adjpos] == FALSE_EYE) {
						board[pos] = PARTIAL_EYE;
						break;
					}
				}
			}
		}
	}

	static private void buildFullEye() {

		for (Integer pos : terrList) {
			if (board[pos] == EMPTY) {
				board[pos] = nearTo(pos);
			}

		}
	}

	static private boolean[] accessed = new boolean[MAX_SIZE];

	static private int nearTo(int pos) {
		Arrays.fill(accessed, false);
		LinkedList<Integer> curLayer = new LinkedList<Integer>();
		LinkedList<Integer> nextLayer = new LinkedList<Integer>();

		curLayer.addLast(pos);
		while (!curLayer.isEmpty()) {
			boolean hasMyColor = false;
			boolean hasOppColor = false;

			for (Integer cur : curLayer) {
				if (!accessed[cur]) {
					accessed[cur] = true;
					if (board[cur] == mycolor) {
						hasMyColor = true;
					}
					if (board[cur] == oppcolor) {
						hasOppColor = true;
					}
					ArrayList<Integer> adj = dumbBrd.getAdjacent(cur);
					for (Integer adjpos : adj) {
						if (!accessed[adjpos])
							nextLayer.addLast(adjpos);
					}
				}
			}

			if (hasMyColor && hasOppColor) {
				return PARTIAL_EYE;
			} else if (hasMyColor && !hasOppColor) {
				return FULL_EYE;
			} else if (!hasMyColor && hasOppColor) {
				return FALSE_EYE;
			}
			LinkedList<Integer> tmp = curLayer;
			tmp.clear();
			curLayer = nextLayer;
			nextLayer = tmp;
		}
		Lib.assertNotReached();
		return FALSE_EYE;
	}

	// ~~~~~~~~~~~~~~~~~~~~build the shape beg
	static private boolean[] accessed2 = new boolean[MAX_SIZE];

	static private LinkedList<Shape> divide() {
		LinkedList<Shape> res = new LinkedList<Shape>();
		Arrays.fill(accessed2, false);

		for (Integer pos : terrList) {
			if (board[pos] == FULL_EYE && !accessed2[pos]) {
				LinkedList eyes = new LinkedList<Integer>();
				DFS(pos, eyes);
				res.addLast(Shape.specify(eyes));
			}
		}
		return res;
	}

	static private void DFS(int pos, LinkedList<Integer> stones) {
		if (accessed2[pos])
			return;
		accessed2[pos] = true;
		ArrayList<Integer> adj = dumbBrd.getAdjacent(pos);
		for (Integer adjpos : adj) {
			if (board[adjpos] == FULL_EYE)
				DFS(adjpos, stones);
		}
	}

	// ~~~~~~~~~~~~~~~~~~~~build the shape end

	static private State getState() {
		int partial_eyeN = 0;
		int full_eyeN = 0;
		for (Integer pos : terrList) {
			switch (board[pos]) {
			case FULL_EYE:
				++full_eyeN;
				break;
			case PARTIAL_EYE:
				++partial_eyeN;
				break;
			}
		}
		return new State(partial_eyeN, full_eyeN, divide());
	}
}
